package away3d.animators  ;


import away3d.animators.skeleton.SkeletonNaryLERPNode  ;
import away3d.animators.skeleton.SkeletonTimelineClipNode  ;
import away3d.animators.skeleton.SkeletonTreeNode  ;
import away3d.animators.data.SkeletonAnimationSequence  ;
import away3d.animators.data.SkeletonAnimationState  ;
import away3d.animators.data.AnimationSequenceBase  ;



/**
 * AnimationSequenceController provides a controller for single clip-based animation sequences (fe: md2, md5anim).
 */
class SmoothSkeletonAnimator extends SkeletonAnimatorBase
{
    
    
    private var _clips              : Array< ? >  ;//TODO:
    private var _activeClipIndex    : Int  ;
    private var _fadeOutClips       : Vector<Int>  ;
    private var _fadeOutSpeeds      : Vector<Float>  ;
    private var _lerpNode           : SkeletonNaryLERPNode  ;
    private var _crossFadeTime      : Float  ;
    private var _mainWeight         : Float  ;
    
    
    
    /**
     * Creates a new AnimationSequenceController object.
     */
    public function new( target: SkeletonAnimationState )
    {
        
        super( target )  ;
        
        _activeClipIndex    = -1  ;
        _mainWeight         = 1  ;
        _clips              = []  ;
        _fadeOutClips       = new Vector()  ;
        _fadeOutSpeeds      = new Vector()  ;
        
    }
    
    
    override protected function createBlendTree() : SkeletonTreeNode
    {
        
        _lerpNode = new SkeletonNaryLERPNode() ;
        return _lerpNode ;
        
    }
    
    /**
     * Plays a sequence with a given name. If the sequence is not found, it may not be loaded yet, and it will retry every frame.
     * @param sequenceName The name of the clip to be played.
     */
    public function play( sequenceName : String, ?crossFadeTime : Float = 0 ) 
    {
        
        var clip : SkeletonTimelineClipNode ;
        
        _crossFadeTime = crossFadeTime ;
        
        if (_activeClipIndex != -1) 
        {
            
            _fadeOutClips.push( _activeClipIndex ) ;
            // diminish per second
            _fadeOutSpeeds.push( _mainWeight / crossFadeTime / 1000 ) ;
            
        }
        
        clip = _clips[ sequenceName ] ;
        clip.reset() ;
        
        if( clip && clip.duration > 0 )
        {
            
            _activeClipIndex    = _lerpNode.getInputIndex( clip ) ;
            var i               = _fadeOutClips.indexOf( _activeClipIndex ) ;
            
            if( i != -1 )
            {
                
                _fadeOutClips.splice(  i, 1 ) ;
                _fadeOutSpeeds.splice( i, 1 ) ;
                
            }
            
        }
        
        if( _activeClipIndex == -1 )
        {
            
            throw new Error( "Clip not found!" ) ;
            
        }
        
        start() ;
        
    }
    
    
    /**
     * Adds a sequence to the controller.
     */
    public function addSequence( sequence : SkeletonAnimationSequence ) 
    {
        var node                = new SkeletonTimelineClipNode() ;
        _clips[ sequence.name ] = node ;
        node.clip               = sequence ;
        
        _lerpNode.addInput( node ) ;
        
    }
    
    /**
     * @inheritDoc
     * @private
     */
    override protected function updateAnimation(realDT : Float, scaledDT : Float) 
    {
        
        var blendTree : SkeletonTreeNode ;
        
        updateWeights(realDT) ;
        
        blendTree       = _target.blendTree ;
        _lerpNode.time  += scaledDT / _lerpNode.duration ;
        
        super.updateAnimation(realDT, scaledDT) ;
        
    }
    
    
    private function updateWeights( dt : Float ) 
    {
        var weight  : Float ;
        var len                         = _fadeOutClips.length ;
        var weights : Vector<Float>     = _lerpNode.blendWeights ;
        var total                       = 0 ;
        var speed   : Float ;
        var index   : Int ;
        
        for( i in 0...len )
        {
            
            index   = _fadeOutClips[ i ] ;
            speed   = _fadeOutSpeeds[ i ] * dt ;
            weight  = weights[ index ] - speed ;
            
            if( weight <= 0 )
            {
                
                weight = 0 ;
                _fadeOutClips.splice(  i, 1 ) ;
                _fadeOutSpeeds.splice( i, 1 ) ;
                --len ;
                
            }
            else 
            {
                
                ++i ;
                
            }
            
            weights[ index ]    = weight ;
            total               += weight ;
            
        }
        
        weights[ _activeClipIndex ] = _mainWeight = 1 - total ;
        _lerpNode.updateWeights( weights ) ;
        
    }
    
    
}